package com.mask.im.business.interception;

import com.alibaba.fastjson.JSON;
import com.mask.im.business.annotation.AccessBarrier;
import com.mask.im.business.model.entity.UserInfo;
import com.mask.im.business.common.exception.BusinessException;
import com.mask.im.business.mapper.UserInfoMapper;
import com.mask.im.business.model.dto.DisableDTO;
import com.mask.im.business.common.model.RestResponse;
import com.mask.im.business.service.RedisService;
import com.mask.im.business.common.util.IpUtil;
import com.mask.im.business.common.util.MaskIMSecurityUtils;
import com.mask.im.business.common.util.TimeUtils;
import com.mask.token.util.MaskSecurityUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.mask.im.business.common.constant.CommonConstant.APPLICATION_JSON;
import static com.mask.im.business.common.constant.CommonConstant.TEST_BASE;
import static com.mask.im.business.common.constant.RedisConstant.BAN;
import static com.mask.im.business.common.constant.RedisConstant.BLOCKING_ACCESS;
import static com.mask.im.business.enums.StatusCodeEnum.BLACKLIST;

/**
 * 对接口进行限流访问
 */
@Slf4j
@Component
public class LimitInterception implements HandlerInterceptor {

	@Resource
	private UserInfoMapper userInfoMapper;

	@Resource
	private RedisService redisService;


	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		Object hGet = redisService.hGet(BLOCKING_ACCESS, BAN + MaskIMSecurityUtils.getUserId());
		if (Objects.nonNull(hGet)) {
			throw new BusinessException(BLACKLIST);
		}
		if (handler instanceof HandlerMethod) {
			HandlerMethod methodHandle = (HandlerMethod) handler;
			AccessBarrier accessBarrier = methodHandle.getMethodAnnotation(AccessBarrier.class);
			if (accessBarrier != null) {
				int size = accessBarrier.size();
				long limitTime = accessBarrier.limitTime();
				String key = IpUtil.getIpAddress(request) + "-" + request.getRequestURI() + "-" + MaskIMSecurityUtils.getUserId();
				Long incr = redisService.incr(key, 1);
				if (incr <= size) {
					redisService.expire(key, limitTime);
				} else {
					Long expire = redisService.getExpire(key);
					if (incr == size + (limitTime<50L?8:80)){
						redisService.hSet(BLOCKING_ACCESS, BAN + MaskIMSecurityUtils.getUserId(), LocalDateTime.now(),60*60*24*7);
						attackDetection(response);
						return false;
					}
					throw new BusinessException("请求过于频繁,请" + TimeUtils.formatTime(expire) + "稍后重试");
				}
			}
		}
		return true;
	}

	@SneakyThrows
	private void attackDetection(HttpServletResponse response) {
		UserInfo userInfo = UserInfo.builder()
				.id(MaskSecurityUtil.getCurrentUserId())
				.disable(0).build();
		userInfoMapper.updateById(userInfo);
		String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
			Map<String, Object> map = new HashMap<>();
			map.put("content", "<span style='color:red'>账号封禁通知：</span>"
					+ "检测到您在" + format + "恶意发送请求，账号已被封禁,如需回复,请点击！" +
					"<a style='text-decoration: underline;color:blue' href="+ TEST_BASE +">点击申述</a>");
			DisableDTO disableDTO = new DisableDTO();
			disableDTO.setId(MaskIMSecurityUtils.getUserId());
			disableDTO.setSubject("账号封禁通知");
			disableDTO.setTemplate("disable.html");
			disableDTO.setCommentMap(map);
//			rabbitTemplate.convertAndSend(RabbitMQConfig.DISABLE_EXCHANGE, "disable.ban"
//					, new Message(JSON.toJSONBytes(disableDTO), new MessageProperties()));
		response.setContentType(APPLICATION_JSON);
		RestResponse<Object> RestResponse = com.mask.im.business.common.model.RestResponse.fail(-50003, "检查到您恶意攻击，账号被冻结7天！");
		response.getWriter().write(JSON.toJSONString(RestResponse));
	}
}
